/*
Copyright 2008  Four Roads LLC conact@4-roads.com

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either 
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Web;
using CommunityServer.Configuration;
using CommunityServer.Components;
using System.Collections;
using System.Web.Caching;

namespace FourRoads.Links
{
	using DataProvider;

	public enum LinkViewSort
	{
		Name = 0,
		Popular = 1,
		Newest = 2,
		Rating = 3
	}
	/// <summary>
	/// Summary description for ResLinkLink.
	/// </summary>
	[Serializable()]
	public class ResLinkLink : CommunityServer.Components.Post , IThread , IComparable
	{
		public static ResLinkLink GetSingleLink(int linkID)
		{
			return GetSingleLink(  CSContext.Current.SectionID  , linkID );
		}

		public static ResLinkLink GetSingleLink(int sectionID , int linkID )
		{
			string cacheKey = string.Format("ResLinks-Posts:s:{0}:p:{0}" ,sectionID , linkID );

			ResLinkLink sc = CSCache.Get(cacheKey) as ResLinkLink;
			if(sc == null)
			{
				ResLinkDataProvider  rldp = ResLinkDataProvider.Instance();

				sc = rldp.GetLink(sectionID , linkID);

				CSCache.Insert(cacheKey , sc  ,  null , CSCache.MinuteFactor ,CacheItemPriority.Low);

				ResLinkSection.ClearLinksCache(sectionID);
			}

			return sc;
		}

		public ResLinkLink()
		{
			try
			{
				UserHostAddress = CSContext.Current.Context.Request.UserHostAddress;
			}
			catch(Exception)
			{

			}
		}

		public ResLinkLink(Section parentSection)
		{
			Section = parentSection;
			UserHostAddress = CSContext.Current.Context.Request.UserHostAddress;
		}

		public static void ClearCommentsCache(int postID)
		{
			CSCache.RemoveByPattern( string.Format("ResLinks-Comments:p:{0}" ,postID) );
		}

		public int CommentModerationCount = 0;

		public ResLinkComments[] Comments
		{
			get
			{
				//Get all of the comments and put them in the cache
				//make the cache dependant on the link
				bool moderator = (ResLinkPermission.LightAccessCheck((ResLinkSection)Section, (Permission)ResLinkPermissionEnum.Moderate , CSContext.Current.User ) 
					|| (ExtendedUserRoles.IsLinkAdministrator || CSContext.Current.User.IsModerator));

				//try and get the links ffrom the cache if that fails then get them from the database
				string cacheKey = string.Format("ResLinks-Comments:p:{0}:Comments:m:{1}" , PostID , moderator);

				ResLinkComments[] comments = CSCache.Get(cacheKey) as ResLinkComments[];

				if (comments == null)
				{
					ResLinkDataProvider dp = ResLinkDataProvider.Instance();

					ArrayList cmts = dp.GetLinkComments(SectionID , PostID  , moderator);

					comments = (ResLinkComments[])cmts.ToArray(typeof(ResLinkComments));

					CSCache.Insert(cacheKey , comments  , null  , CSCache.MinuteFactor / 2,CacheItemPriority.Low);
				}

				CommentCount = comments.Length;

				return comments;
			}
		}

		private string _LinkURL = string.Empty;
		private bool _ReportedDead = false;
		private int _ReportingUserID = -1;
		private int _Rating = 0;
		private int _CommentsCount = 0;

		public int Rating
		{
			get
			{
				return _Rating;
			}
			set
			{
				_Rating = value;
			}
		}
	

		public int CommentCount
		{
			get
			{
				return _CommentsCount;
			}
			set
			{
				_CommentsCount = value;
			}
		}

		public int ReportingUserID
		{
			get
			{
				return _ReportingUserID;
			}
			set
			{
				_ReportingUserID = value;
			}
		}

		public bool ReportedDead
		{
			get
			{
				return _ReportedDead;
			}
			set
			{
				_ReportedDead = value;
			}
		}

		public string LinkUrl
		{
			get
			{
				return _LinkURL;
			}
			set
			{
				_LinkURL = value;
			}
		}

		#region Section
		private  ResLinkSection section = null; 

		/// <summary>
		/// Override Section. Returns the partent "Section" for this post if it is part of a tree
		/// </summary>
		public override  CommunityServer.Components.Section Section
		{
			get
			{
				if (section == null)
				{
					section = ResLinkSection.GetSection( SectionID);
				}

				return section;
			}
			set
			{
				section = value as ResLinkSection;
			}
		}

		#endregion

		#region IThread Privates
		string mostRecentAuthor = "";   // Most recent post author
		int mostRecentAuthorID = 0;
		int mostRecentPostID = 0;       // Most recent post id
		int authorID = 0;
		#endregion

		#region IThread Public Properties
		ThreadStatus status = ThreadStatus.NotSet;
		public ThreadStatus Status 
		{
			get { return status; }
			set { status = value; }
		}

		public int AuthorID 
		{
			get 
			{
				return authorID;
			}
			set 
			{
				authorID = value;
			}
		}

		// *********************************************************************
		//
		//  IsPopular
		//
		/// <summary>
		/// If thread has activity in the last 2 days and > 20 replies
		/// </summary>
		//
		// ********************************************************************/
		public bool IsPopular 
		{
			get 
			{ 
				if ((ThreadDate < DateTime.Now.AddDays(5)) && ( (Replies > 3 || (Views > 20) )))
					return true;

				return false;
			}
		}
        
        
		// *********************************************************************
		//
		//  IsAnnouncement
		//
		/// <summary>
		/// Not a chance as its a link
		/// </summary>
		//
		// ********************************************************************/
		public bool IsAnnouncement 
		{
			get 
			{ 
				return false;
			}
		}


		bool isSticky = false;
		public bool IsSticky 
		{
			get 
			{
				return isSticky;
			}
			set 
			{
				isSticky = value;
			}
		}

		DateTime stickyDate = DateTime.Now.AddYears(-25);
		public DateTime StickyDate 
		{
			get 
			{
				return stickyDate;
			}
			set 
			{
				stickyDate = value;
			}
		}


		// *********************************************************************
		//
		//  MostRecentPostAuthor
		//
		/// <summary>
		/// The author of the most recent post in the thread.
		/// </summary>
		//
		// ********************************************************************/
		public string MostRecentPostAuthor 
		{
			get 
			{ 
				return mostRecentAuthor; 
			}
			set 
			{ 
				mostRecentAuthor = value; 
			}
		}

		public int MostRecentPostAuthorID 
		{
			get 
			{
				return mostRecentAuthorID;
			}
			set 
			{
				mostRecentAuthorID = value;
			}
		}

		//Not used
		public string PreviewBody 
		{
			get 
			{
				return "";
			}
		}

		// *********************************************************************
		//
		//  MostRecentPostID
		//
		/// <summary>
		/// The post id of the most recent post in the thread.
		/// </summary>
		//
		// ********************************************************************/
		public int MostRecentPostID 
		{
			get 
			{ 
				return mostRecentPostID; 
			}
			set 
			{ 
				mostRecentPostID = value; 
			}
		}

		#endregion

		public void UpdateComment(ResLinkComments comment)
		{
			ResLinkPermission.AccessCheck(section, ResLinkPermissionEnum.ViewLink , CSContext.Current.User );
			ResLinkPermission.AccessCheck(section, ResLinkPermissionEnum.Moderate , CSContext.Current.User );

			ResLinkDataProvider  rldp = ResLinkDataProvider.Instance();

			CSEvents.PrePost(comment,ObjectState.Update,(ApplicationType)ExApplicationType.ResLink);
			CSEvents.BeforePost(comment,ObjectState.Update,(ApplicationType)ExApplicationType.ResLink);

			rldp.UpdateComment(comment);

			CSEvents.AfterPost(comment,ObjectState.Update,(ApplicationType)ExApplicationType.ResLink);

			//Notify anyone watching this item
			ResLinkEmails.LinkCommentTracking(comment , this ,  section);

			ClearCommentsCache(comment.ParentID);
		}

		public void ApproveComment(ResLinkComments comment)
		{
			//Moderator just approve it
			ResLinkPermission.AccessCheck(section , ResLinkPermissionEnum.Moderate , CSContext.Current.User );
		
			bool previousState = comment.IsApproved ;

			comment.IsApproved = true;

			UpdateComment(comment);

			if (previousState == false)
				ResLinkEmails.LinkCommentApproved(this , comment);

			ClearCommentsCache(comment.ParentID);
		}


		static public ResLinkComments AddComment(ResLinkSection section , ResLinkComments comment , int parentPost)
		{
			ResLinkPermission.AccessCheck(section, ResLinkPermissionEnum.AddComments , CSContext.Current.User );

			ResLinkDataProvider  rldp = ResLinkDataProvider.Instance();

			comment.IsApproved = ResLinkPermission.LightAccessCheck(section, ResLinkPermissionEnum.Moderate , CSContext.Current.User ) 
				|| ExtendedUserRoles.IsLinkAdministrator || CSContext.Current.User.IsModerator
				|| !section.IsModerated ||  CSContext.Current.User.ModerationLevel == ModerationLevel.Unmoderated;

			comment.SectionID = section.SectionID;
			comment.ParentID = parentPost;
			comment.User = CSContext.Current.User;
			comment.PostLevel = 2;

			CSEvents.PrePost(comment,ObjectState.Create,(ApplicationType)ExApplicationType.ResLink);
			CSEvents.BeforePost(comment,ObjectState.Create,(ApplicationType)ExApplicationType.ResLink);

			comment.PostID = rldp.AddComment(comment);

			CSEvents.AfterPost(comment,ObjectState.Create,(ApplicationType)ExApplicationType.ResLink);

			//Notify anyone watching this item
			ResLinkEmails.LinkCommentTracking(comment , section.FindLink(parentPost) , section);

			ClearCommentsCache(comment.ParentID);

			return comment;
		}

		public void DeleteComment(int sectionID , ResLinkComments comment  , int userID , string reason)
		{
			if (comment.User.UserID == CSContext.Current.User.UserID 
				|| ResLinkPermission.LightAccessCheck(section, ResLinkPermissionEnum.Moderate , CSContext.Current.User )
				|| ExtendedUserRoles.IsLinkAdministrator)
			{
				ResLinkDataProvider  rldp = ResLinkDataProvider.Instance();

				CSEvents.PrePost(comment,ObjectState.Delete,(ApplicationType)ExApplicationType.ResLink);
				CSEvents.BeforePost(comment,ObjectState.Delete,(ApplicationType)ExApplicationType.ResLink);

				rldp.DeleteComment(sectionID , comment.PostID , PostID , reason );

				CSEvents.AfterPost(comment,ObjectState.Delete,(ApplicationType)ExApplicationType.ResLink);

				//Correct the mathmatics
				RatingSum -= comment.CommentRating;
				TotalRatings --;
				
				ResLinkEmails.LinkCommentDeleted(this , comment , reason);

				ClearCommentsCache(comment.ParentID);
			}
		}

		public void DeleteComment(int sectionID , int commentID )
		{
			ResLinkComments com = null;
			foreach(ResLinkComments c in Comments)
			{
				if (c.PostID == commentID)
				{
					com = c;
					break;
				}
			}
	
			if (com != null)
			{
				//if we are the owner then just delete the comment with a owner deleted message
				if (com.User.UserID == CSContext.Current.User.UserID)
				{
					DeleteComment(sectionID , com , com.User.UserID , "Owner retracted comment"); //no email is required here

					ClearCommentsCache(com.PostID);
				}//else if we are a moderator then go to the delete reason page
				else if(ResLinkPermission.LightAccessCheck(section, ResLinkPermissionEnum.Moderate , CSContext.Current.User ))
				{
					
				}
				else
				{
					ResLinkPermission.AccessCheck(section, ResLinkPermissionEnum.Moderate , CSContext.Current.User );
				}
			}
		}
		#region IComparable Members

		public int CompareTo(object obj)
		{
			if (obj is ResLinkLink)
			{
				ResLinkLink lnk = obj as ResLinkLink;

				return this.PostID - lnk.PostID;
			}
			else
			{
				return this.PostID - (int)obj;
			}
		}

		#endregion
	}
}
